
package la.foton.atividade.util.exception;

import java.util.Iterator;
import java.util.Map;
import javax.faces.FacesException;
import javax.faces.application.FacesMessage;
import javax.faces.application.FacesMessage.Severity;
import javax.faces.application.NavigationHandler;
import javax.faces.application.ViewExpiredException;
import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerWrapper;
import javax.faces.context.FacesContext;
import javax.faces.event.ExceptionQueuedEvent;
import javax.faces.event.ExceptionQueuedEventContext;
import javax.faces.event.PhaseId;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import la.foton.atividade.dao.ExceptionInterceptor;
//import org.primefaces.component.PartialViewRoot;
//import org.primefaces.context.RequestContextImpl;
//import org.primefaces.context.RequestContext;
//import org.primefaces.util.Constants;
import la.foton.atividade.util.PropertiesUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.MailSender;

/**
 * Esta classe é utilizada para tratar
 * 
 * @author Dezembro/2013: Gleyson Carlos dos Santos <DD>
 */
public class ExceptionExceptionHandler extends ExceptionHandlerWrapper
{

   private static final Logger logger = LoggerFactory.getLogger(ExceptionInterceptor.class);
   @Autowired
   MailSender mailSender;

   private ExceptionHandler wrapped;

   // Obtém uma instância do FacesContext
   final FacesContext facesContext = FacesContext.getCurrentInstance();

   // Obtém um mapa do FacesContext
   final Map requestMap = facesContext.getExternalContext().getRequestMap();

   // Obtém o estado atual da navegação entre páginas do JSF
   final NavigationHandler navigationHandler = facesContext.getApplication()
            .getNavigationHandler();

   // Declara o construtor que recebe uma exceptio do tipo ExceptionHandler
   // como parâmetro
   ExceptionExceptionHandler(ExceptionHandler exception)
   {
      this.wrapped = exception;
   }

   // Sobrescreve o método ExceptionHandler que retorna a "pilha" de exceções
   @Override
   public ExceptionHandler getWrapped()
   {
      return wrapped;
   }

   // Sobrescreve o método handle que é responsável por manipular as exceções
   // do JSF
   @Override
   public void handle() throws FacesException
   {
      HttpServletRequest request = (HttpServletRequest) facesContext.getExternalContext().getRequest();
      String errorCode = request.getParameter("error");

      // Validação da fase porque este metodo é chamado em todas as fases e faria o tratamento em cada uma delas.
      if (errorCode != null && facesContext.getCurrentPhaseId() == PhaseId.INVOKE_APPLICATION)
      {
         handleSessionExpiredAjaxException(errorCode);
      }
      else
      {
         final Iterator iterator = getUnhandledExceptionQueuedEvents().iterator();
         while (iterator.hasNext())
         {
            ExceptionQueuedEvent event = (ExceptionQueuedEvent) iterator.next();
            ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event
                     .getSource();

            // Recupera a exceção do contexto
            Throwable exception = context.getException();

            try
            {
               if (exception instanceof ViewExpiredException)
               {
                  handleViewExpiredException(exception);
               }
               else if (exception instanceof TreatedException)
               {
                  handleTreatedException(exception);
               }
               else
               {
                  handleDefaultException(exception);
               }

            }
            finally
            {
               iterator.remove();
            }
         }
      }
      // Manipula o erro
      getWrapped().handle();
   }

   /**
    * Redireciona para a pagina de erro correspondente
    * 
    * @param redirectPage
    */
   protected void doRedirect(String redirectPage)
   {

      // Seta a navegação para uma página padrão.
      navigationHandler.handleNavigation(facesContext, null, redirectPage);

      // Renderiza a pagina de erro e exibe as mensagens
      facesContext.renderResponse();
   }

   /**
    * Adiciona mensagem de erro
    * 
    * @param componentId
    * @param errorMessage
    */
   protected void addErrorMessage(String componentId, String errorMessage)
   {
      addMessage(componentId, errorMessage, FacesMessage.SEVERITY_ERROR);
   }

   /**
    * Adiciona mensagem de erro
    * 
    * @param errorMessage
    */
   protected void addErrorMessage(String errorMessage)
   {
      addErrorMessage(null, errorMessage);
   }

   /**
    * adiciona mensagem de informacao
    * 
    * @param componentId
    * @param infoMessage
    */
   protected void addInfoMessage(String componentId, String infoMessage)
   {
      addMessage(componentId, infoMessage, FacesMessage.SEVERITY_INFO);
   }

   /**
    * Adiciona mensagem de informacao
    * 
    * @param infoMessage
    */
   protected void addInfoMessage(String infoMessage)
   {
      addInfoMessage(null, infoMessage);
   }

   /**
    * Metodo comum
    * 
    * @param componentId
    * @param errorMessage
    * @param severity
    */
   private void addMessage(String componentId, String errorMessage,
      Severity severity)
   {
      FacesMessage message = new FacesMessage(errorMessage);
      message.setSeverity(severity);
      facesContext.addMessage(componentId, message);
   }

   private void handleViewExpiredException(Throwable exception)
   {

      ViewExpiredException vee = (ViewExpiredException) exception;
      FacesContext fc = FacesContext.getCurrentInstance();
      HttpSession session = (HttpSession) fc.getExternalContext().getSession(
         true);

      // Push some useful stuff to the request scope for use in the page
      session.setAttribute("currentViewId", vee.getViewId());

      // redireciona para pagina escolhida
      doRedirect("/index.jsf");

      // Importante, colocar a variável de controle exibeDialog na
      // sessão para posteriormente ser capturada nos managedBean e
      // ser exibido um dialog na respectiva página xhtml.
      session.setAttribute("exibeDialog", true);
   }

   private void handleTreatedException(Throwable exception)
   {

   }

   /**
    * Este método é utilizado para colocar a mensagem vinda do filtro AjaxTimeoutRedirect, quando a sessão está expirada e a requisição foi
    * feita via ajax.
    * 
    * @param errorCode
    */
   private void handleSessionExpiredAjaxException(String errorCode)
   {
      String erroMessage = PropertiesUtil.MESSAGE_ERROR.getValue(errorCode);
      logger.debug("ErrorCode recuperado" + errorCode + ": " + erroMessage);
      addErrorMessage(erroMessage);
   }

   private void handleDefaultException(Throwable exception)
   {
      addErrorMessage("ERRO!");
   }

}
